// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <errno.h>
#include <fcntl.h>
#include <zircon/assert.h>
#include <fbl/auto_call.h>
#include <fbl/algorithm.h>
#include <lib/fdio/io.h>
#include <stdio.h>

#include "wav-source.h"

constexpr uint32_t WAVCommon::RIFF_FOUR_CC;
constexpr uint32_t WAVCommon::WAVE_FOUR_CC;
constexpr uint32_t WAVCommon::FMT_FOUR_CC;
constexpr uint32_t WAVCommon::DATA_FOUR_CC;

constexpr uint16_t WAVCommon::FORMAT_UNKNOWN;
constexpr uint16_t WAVCommon::FORMAT_LPCM;
constexpr uint16_t WAVCommon::FORMAT_MSFT_ADPCM;
constexpr uint16_t WAVCommon::FORMAT_IEEE_FLOAT;
constexpr uint16_t WAVCommon::FORMAT_MSFT_ALAW;
constexpr uint16_t WAVCommon::FORMAT_MSFT_MULAW;

zx_status_t WAVCommon::Initialize(const char* filename, InitMode mode) {
  if (fd_ >= 0) {
    printf("Failed to initialize WAVCommon for \"%s\", already initialized\n", filename);
    return ZX_ERR_BAD_STATE;
  }

  int flags = (mode == InitMode::SOURCE) ? O_RDONLY : O_RDWR | O_CREAT;
  fd_ = ::open(filename, flags);
  if (fd_ < 0) {
    printf("Failed to open \"%s\" (res %d)\n", filename, fd_);
    return static_cast<zx_status_t>(fd_);
  }

  return ZX_OK;
}

void WAVCommon::Close() {
  if (fd_ >= 0) {
    ::close(fd_);
    fd_ = -1;
  }
}

zx_status_t WAVCommon::Read(void* buf, size_t len) {
  if (fd_ < 0)
    return ZX_ERR_BAD_STATE;

  ssize_t res = ::read(fd_, buf, len);
  if (res < 0) {
    printf("Read error (errno %d)\n", errno);
    return static_cast<zx_status_t>(errno);
  }

  if (static_cast<size_t>(res) != len) {
    printf("Short read error (%zd < %zu)\n", res, len);
    return ZX_ERR_IO;
  }

  return ZX_OK;
}

zx_status_t WAVCommon::Write(const void* buf, size_t len) {
  if (fd_ < 0)
    return ZX_ERR_BAD_STATE;

  ssize_t res = ::write(fd_, buf, len);
  if (res < 0) {
    printf("Write error (errno %d)\n", errno);
    return static_cast<zx_status_t>(errno);
  }

  if (static_cast<size_t>(res) != len) {
    printf("Short write error (%zd < %zu)\n", res, len);
    return ZX_ERR_IO;
  }

  return ZX_OK;
}

zx_status_t WAVCommon::Seek(off_t abs_pos) {
  if (fd_ < 0)
    return ZX_ERR_BAD_STATE;
  if (abs_pos < 0)
    return ZX_ERR_INVALID_ARGS;

  off_t res = ::lseek(fd_, abs_pos, SEEK_SET);
  if (res < 0) {
    printf("Seek error (errno %d)\n", errno);
    return static_cast<zx_status_t>(errno);
  }

  if (res != abs_pos) {
    printf("Failed to see to target (target %zd, got %zd)\n", static_cast<ssize_t>(abs_pos),
           static_cast<ssize_t>(res));
    return ZX_ERR_IO;
  }

  return ZX_OK;
}
